package com.shengtsource.minicat;

import com.shengtsource.minicat.mapper.Mapper;
import com.shengtsource.minicat.mapper.MapperContext;
import com.shengtsource.minicat.mapper.MapperHost;
import com.shengtsource.minicat.mapper.MapperWapper;
import com.shengtsource.minicat.model.HttpServlet;
import com.shengtsource.minicat.processor.RequestProcessor;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * Minicat 启动类
 * Minicat 需完成以下几件事
 * 1.监听端口，接收请求（socket通信)
 * 2.请求数据封装为Request对象，响应数据封装为Response对象
 * 3.区分处理静态资源和动态资源
 * 4.返回资源给客户端浏览器
 * @author shengtsource
 */
public class BootStrap {
    private static int port = 8080;//默认端口8080

    private static List<Mapper> mappers = new ArrayList<>();//server配置信息

    /**
     * Servlet映射集合
     */
    private static Map<String, HttpServlet> servletMap = new HashMap<>();

    private ThreadPoolExecutor executor = new ThreadPoolExecutor(50, 100, 60000l, TimeUnit.MILLISECONDS,
            new ArrayBlockingQueue<Runnable>(100), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

    static {
        // 加载servletMap
        try {
            loadServerConfig();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 加载server配置
     */
    private static void loadServerConfig() throws Exception {
      InputStream in = BootStrap.class.getClassLoader().getResourceAsStream("server.xml");
      SAXReader saxReader = new SAXReader();
      Document document = saxReader.read(in);
      Element rootElement = document.getRootElement();
      List<Element> serviceNode = rootElement.selectNodes("/Server/Service");
        for (Element element : serviceNode) {
            //解析Connector标签，设置Service服务端口号
            Element connectorEle= (Element) element.selectSingleNode("/Server/Service/Connector");
            int port = Integer.parseInt(connectorEle.attributeValue("port"));
            Mapper mapper = new Mapper();
            mapper.setPort(port);
            List<MapperHost> mapperHosts = new ArrayList<>();

            //解析Host标签，封装Host主机配置
            List<Element> hostEles = element.selectNodes("/Server/Service/Engine/Host");
            for (Element hostEle : hostEles) {
                String name = hostEle.attributeValue("name");
                String appBase = hostEle.attributeValue("appBase");
                MapperHost mapperHost = new MapperHost();
                mapperHost.setName(name);
                mapperHost.setAppBase(appBase);

                //加载servlet
                loadServlet(mapperHost);

                mapperHosts.add(mapperHost);
            }
            mapper.setObject(mapperHosts);
            mappers.add(mapper);
        }
    }

    /**
     * 加载webapps目录下web.xml文件
     * @param mapperHost
     */
    private static void loadServlet(MapperHost mapperHost) throws Exception {
        //获取appBase绝对路径
        String appBase = mapperHost.getAppBase();
        List<MapperContext> mapperContexts = new ArrayList<>();
        File file = new File(appBase);
        //遍历循环appBase下的目录，一个目录对应一个项目
        if(file.isDirectory()) {
            File[] files = file.listFiles();
            for (File file1 : files) {

                if(!file.isDirectory()) {
                   continue;
                }
                //加载实例化每个应用的Servlet
                MapperContext mapperContext = new MapperContext();
                mapperContext.setName("/"+ file1.getName());
                URL url = file1.toURI().toURL();
                URL[] urls = {url};
                URLClassLoader urlClassLoader = new URLClassLoader(urls);
                doLoadServlet(appBase,mapperContext,urlClassLoader);
                mapperContexts.add(mapperContext);
            }
            mapperHost.setObject(mapperContexts);
        }

    }

    /**
     * 解析配置文件，实例化Servlet
     * @param appBase
     * @param context
     * @throws DocumentException
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private static void doLoadServlet(String appBase,MapperContext context,URLClassLoader urlClassLoader) throws DocumentException, ClassNotFoundException, IllegalAccessException, InstantiationException, FileNotFoundException {
        List<MapperWapper> mapperWappers = new ArrayList<>();
        String webConfigPath = appBase.concat(File.separator).concat(context.getName()).concat("/WEB-INF/web.xml");
        InputStream in = new FileInputStream(new File(webConfigPath));
//        InputStream in = urlClassLoader.getResourceAsStream(webConfigPath);
        SAXReader saxReader = new SAXReader();
        Element root = saxReader.read(in).getRootElement();
        List<Element> selectNodes = root.selectNodes("//servlet");
        for (Element node : selectNodes) {
            Node nameNode = node.selectSingleNode("//servlet-name");
            String servletName = nameNode.getStringValue();
            Node classNode = node.selectSingleNode("//servlet-class");
            String servletClazz = classNode.getStringValue();
            Node serlvetMappingNode = node.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
            Node urlPartternNode = serlvetMappingNode.selectSingleNode("//url-pattern");
            if(urlPartternNode != null) {
                String url = urlPartternNode.getStringValue();
                //servletMap.put(url, (HttpServlet) Class.forName(servletClazz).newInstance());
                HttpServlet servlet = (HttpServlet)urlClassLoader.loadClass(servletClazz).newInstance();
                mapperWappers.add(new MapperWapper(servlet,url));
            }

        }
        context.setObject(mapperWappers);
    }

    /**
     * 加载web.xml中的servlet配置信息
     */
    private static void loadServlet() throws ClassNotFoundException, IllegalAccessException, InstantiationException, DocumentException {
        InputStream in = BootStrap.class.getClassLoader().getResourceAsStream("WEB-INF/web.xml");
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(in);
        Element rootElement = document.getRootElement();
        List<Element> selectNodes = rootElement.selectNodes("//servlet");
        for (Element node : selectNodes) {
            Node nameNode = node.selectSingleNode("//servlet-name");
            String servletName = nameNode.getStringValue();
            Node classNode = node.selectSingleNode("//servlet-class");
            String servletClazz = classNode.getStringValue();
            Node serlvetMappingNode = node.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
            Node urlPartternNode = serlvetMappingNode.selectSingleNode("//url-pattern");
            if(urlPartternNode != null) {
                String url = urlPartternNode.getStringValue();
                servletMap.put(url, (HttpServlet) Class.forName(servletClazz).newInstance());
            }


        }
    }

    public void start() throws Exception {

        // 监听指定端口
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("Minicat 容器启动，端口：" + port);

        /**
         * v1.0 访问http://localhost:8080 浏览器响应输出 hello minicat
         */
//        while (true) {
//            // 接收socket，返回输入流
//            Socket socket = serverSocket.accept();
//            //获取输出流，响应信息
//            OutputStream outputStream = socket.getOutputStream();
//            String data = "Hello Minicat";
//            String respText = HttpProtocolUtils.getResponseHeader200(data.getBytes().length).concat(data);
//            outputStream.write(respText.getBytes());
//            socket.close();
//        }

        /**
         * v2.0 访问http://localhost:8080/index.jsp 封装请求和响应对象，返回静态资源
         */
//        while (true) {
//            // 接收socket，返回输入流
//            Socket socket = serverSocket.accept();
//            // 获取输入流，封装Request对象
//            Request request = new Request(socket.getInputStream());
//            System.out.println("URL: "+ request.getUrl() +",Method: " + request.getMethod());
//            // 封装Response对象
//            Response response = new Response(socket.getOutputStream());
//            response.outputHtml(request.getUrl());
//            socket.close();
//        }

        /**
         * v3.0 区别处理静态资源与动态资源
         */
        while (true) {
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket,mappers.get(0));
            executor.execute(requestProcessor);
        }
    }

    public static void main(String[] args)  {
        try {
            new BootStrap().start();
        } catch (IOException e) {
            e.printStackTrace();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}
